/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.wsinfra.wsrm.storage.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Vector;

import cgl.narada.wsinfra.wsrm.storage.WsrmSequenceInfo;
import cgl.narada.wsinfra.wsrm.storage.WsrmSequenceInfoStorageOperations;
import cgl.narada.wsinfra.wsrm.storage.WsrmStorageException;


/**
 * <p>Title: WsrmSequenceInfoStorageOperationsImpl</p>
 * <p>Description: Handles Sequence Info Storage operations</p>
 * @author Beytullah Yildiz
 */

public class WsrmSequenceInfoStorageOperationsImpl
    implements WsrmSequenceInfoStorageOperations {

  private WsrmQueryHandler wsrmQueryHandler;
  private String moduleName = "WsrmSequenceInfoStorageOperationsImpl: ";

  public WsrmSequenceInfoStorageOperationsImpl() {
  }

  public WsrmSequenceInfoStorageOperationsImpl(Connection connection) {
    wsrmQueryHandler = new WsrmQueryHandler(connection);

  }

  /** Stores the sequence identifier that should be used while communicating
      with a given destination (based on the contents of the wsa:To element).
      This feature allows us to send messages reliably with minimal
      intervention from the application.*/
  public void
      addDestinationSequenceIdentifierPair(String destination,
                                           String sequenceIdentifier) throws
      WsrmStorageException {

    String query =
        "insert into protocolstorageoperation values('" +
        sequenceIdentifier + "',"
        + "'" + destination + "')";

    PreparedStatement pstmt =
        wsrmQueryHandler.getPreparedStatement(query);
    try {

      pstmt.executeUpdate();
      pstmt.close();

    }
    catch (SQLException ex) {
      throw new WsrmStorageException(moduleName +
          "addDestinationSequenceIdentifierPair exception ["
          + query + "] " + ex.toString());
    }

  }

  /** Retrieve the sequence identifier for a specific destination */
  public String getSequenceIdentifierForDestination(String destination) throws
      WsrmStorageException {
    String query =
        "select sequenceId from protocolstorageoperation where destination='"
        + destination + "'";

    PreparedStatement preparedStatement =
        wsrmQueryHandler.getPreparedStatement(query);
    ResultSet resultSet = wsrmQueryHandler.getResultSet(preparedStatement);

    String sequenceIdentifier = null;
    try {
       while (resultSet.next()) {
         sequenceIdentifier = resultSet.getString(1);
       }
    }
    catch (SQLException ex) {
      throw new WsrmStorageException(moduleName +
          "getSequenceIdentifierForDestination exception ["
          + query + "] " + ex.toString());

    }

    wsrmQueryHandler.closeResultSet(resultSet);
    wsrmQueryHandler.closePreparedStatement(preparedStatement);
    return sequenceIdentifier;

  }

  /** Retrieves the destination for a specific sequence identifier */
  public String
      getDestinationForSequenceIdentifier(String sequenceIdentifier) throws
      WsrmStorageException {
    String query =
        "select destination from protocolstorageoperation where sequenceId='"
        + sequenceIdentifier + "'";

    PreparedStatement preparedStatement =
        wsrmQueryHandler.getPreparedStatement(query);


    ResultSet resultSet = wsrmQueryHandler.getResultSet(preparedStatement);

    String destination = null;
    try {
      while (resultSet.next()) {
        destination = resultSet.getString(1);
      }
    }
    catch (SQLException ex) {
      throw new WsrmStorageException(moduleName +
          "getSequenceIdentifierForDestination exception ["
          + query + "] " + ex.toString());

    }

    wsrmQueryHandler.closeResultSet(resultSet);
    wsrmQueryHandler.closePreparedStatement(preparedStatement);
    return destination;

  }

  /** Removes the destination/sequence identifier pair corresponding to this
      destination */
  public void
      removeDestinationSequenceIdentifierPair(String destination) throws
      WsrmStorageException {

    String query = "delete from protocolstorageoperation where destination='"
        + destination + "'";
   int row =
        wsrmQueryHandler.updateQuery(query);
    System.out.println("Number of "+row+" deleted");

  }

  /** Checks to see if there is a sequenceId/destination pair corresponding
      to the destination in question. */
  public boolean
      hasSequenceIdForDestination(String destination) throws
      WsrmStorageException {

    String query =
        "select count(*) from protocolstorageoperation where destination='"
        + destination + "'";

    return (wsrmQueryHandler.getLongNumber(query) > 0);

  }

  /** A method which checks whether a certain sequence (irrespective of whether
      it is active/terminated/expired/timed-out) is a known sequence at this
      node */
  public boolean
      isKnownSequence(String sequenceIdentifier) throws WsrmStorageException {
    String query =
        "select count(*) from sequenceinfolight where sequenceId='"
        + sequenceIdentifier + "'";

    return (wsrmQueryHandler.getLongNumber(query) > 0);

  }

  /** Store a SequenceInfo object. This method throws an exception under the
       following circumstances
       <br> The sequenceIdentifier in the SequenceInfo already exists.
       <br> Storage related problems */
  public void storeCreatedSequence(WsrmSequenceInfo sequenceInfo) throws
      WsrmStorageException {
    String sequenceId = sequenceInfo.getSequenceIdentifier();
    String addressingId = sequenceInfo.getAddressingIdentifier();
    byte[] sequenceInfoBinary = sequenceInfo.getBytes();
    boolean sequenceexpired = sequenceInfo.isExpired();
    boolean sequencetimeout = sequenceInfo.isTimedOut();
    boolean sequenceterminated = sequenceInfo.isTerminated();
    boolean sequencesource = sequenceInfo.isSource();
    boolean sequencedestination = sequenceInfo.isDestination();
    boolean sequenceactive = true;
    boolean sequenceserviced = false;

    if (sequenceexpired || sequenceterminated) {
      sequenceactive = false;
    }
    if (sequenceterminated == true && sequenceexpired == false &&
        sequenceterminated == false) {
      sequenceserviced = true;
    }

    String query = "select count(*) from sequenceinfolight where sequenceid='"
        + sequenceId + "'";
    boolean initialDataCreation = false;

    if (wsrmQueryHandler.getLongNumber(query) <= 0) {
      initialDataCreation = true;
    }

    if (initialDataCreation) {
      query =
          "insert into sequenceinfolight  values('" + sequenceId + "',"
          + wsrmQueryHandler.getIntOfBoolean(sequencesource) + "," +
          wsrmQueryHandler.getIntOfBoolean(sequencedestination) + "," +
          wsrmQueryHandler.getIntOfBoolean(sequenceexpired) + "," +
          wsrmQueryHandler.getIntOfBoolean(sequencetimeout) + "," +
          wsrmQueryHandler.getIntOfBoolean(sequenceactive) + "," +
          wsrmQueryHandler.getIntOfBoolean(sequenceterminated) + "," +
          wsrmQueryHandler.getIntOfBoolean(sequenceserviced) + ")";

    }
    else {

      query = "update sequenceinfolight  set " +
          "sequencesource = " + wsrmQueryHandler.getIntOfBoolean(sequencesource) +
          ", sequencedestination = " +
          wsrmQueryHandler.getIntOfBoolean(sequencedestination) +
          ", sequenceexpired = " +
          wsrmQueryHandler.getIntOfBoolean(sequenceexpired) +
          ", sequencetimeout = " +
          wsrmQueryHandler.getIntOfBoolean(sequencetimeout) +
          ", sequenceactive = " +
          wsrmQueryHandler.getIntOfBoolean(sequenceactive) +
          ", sequenceterminated = " +
          wsrmQueryHandler.getIntOfBoolean(sequenceterminated) +
          ", sequenceserviced = " +
          wsrmQueryHandler.getIntOfBoolean(sequenceserviced) +
          " where sequenceid='" + sequenceId + "'";
    }

    PreparedStatement pstmt1 =
        wsrmQueryHandler.getPreparedStatement(query);
    try {

      pstmt1.executeUpdate();
      pstmt1.close();

    }
    catch (SQLException ex) {
      throw new WsrmStorageException(moduleName +
                                     "storeCreatedSequence exception ["
                                     + query + "] " + ex.toString());
    }

    if (initialDataCreation) {
      query = "insert into  sequenceinfofull"
          + " values('" + sequenceId + "','" + addressingId + "', ? )";
    }
    else {
      query = "update sequenceinfofull  set " +
          "addressingid = '" + addressingId +
          "', sequenceinfobinary = ? " +
          " where sequenceid='" + sequenceId + "'";
    }

    PreparedStatement pstmt2 =
        wsrmQueryHandler.getPreparedStatement(query);
    try {
      pstmt2.setBytes(1, sequenceInfoBinary);
      pstmt2.executeUpdate();
      pstmt2.close();

    }
    catch (SQLException ex) {
      throw new WsrmStorageException(moduleName +
                                     "storeCreatedSequence exception ["
                                     + query + "] " + ex.toString());
    }

    System.out.println(moduleName + "Sequence Info which has sequence id <" +
                       sequenceId + "> and addressing id <" + addressingId +
                       ">" +
                       " is stored or updated");
  }

  private Vector getResultSetInVecotorForSequenceInfo(ResultSet resultSet) throws
      WsrmStorageException {
    Vector resultVector = new Vector();

    try {
      while (resultSet.next()) {
        resultVector.add(resultSet.getBytes(1));
      }
    }
    catch (SQLException ex) {
      throw new WsrmStorageException(moduleName +
          " getResultSetInVecotorForSequenceInfo exception " + ex.toString());

    }

    return resultVector;
  }

  private Vector getSequenceInfoQueryResultInVector(String query) throws
      WsrmStorageException {

    PreparedStatement preparedStatement =
        wsrmQueryHandler.getPreparedStatement(query);
    ResultSet resultSet = wsrmQueryHandler.getResultSet(preparedStatement);

    Vector resultSetByteArrayVector = getResultSetInVecotorForSequenceInfo(
        resultSet);

    wsrmQueryHandler.closeResultSet(resultSet);
    wsrmQueryHandler.closePreparedStatement(preparedStatement);
    return resultSetByteArrayVector;
  }

  /** Retrieves the SequenceInfo corresponding to the specified
      sequenceIdentifier. This method throws an exception under the following
      conditions.
      <br> The Sequence specified in sequenceIdentifier is an unknown one.
      <br> Storage related problems */
  public WsrmSequenceInfo
      getSequenceInfoUsingSequenceIdentifier(String sequenceIdentifier) throws
      WsrmStorageException {

    String query =
        "select sequenceinfobinary from sequenceinfofull where sequenceid='"
        + sequenceIdentifier + "'";
    Vector byteArrayVector = getSequenceInfoQueryResultInVector(query);
    if (byteArrayVector.size() == 0) {
      return null;
    }

    byte[] byteArray = (byte[]) byteArrayVector.remove(0);

    WsrmSequenceInfoImpl wsrmSequenceInfoImpl =
        new WsrmSequenceInfoImpl(byteArray);

    return wsrmSequenceInfoImpl;

  }

  /** Retrieves the SequenceInfo corresponding to the ACTIVE sequences with
      the specified sequenceIdentifier prefix.
      This method throws an exception under the following conditions.
      <br> The Sequence specified in sequenceIdentifier is an unknown one.
      <br> Storage related problems */
  public WsrmSequenceInfo[]
      getSequenceInfoUsingSequencePrefix(String sequenceIdentifier) throws
      WsrmStorageException {

    String query =
        "select sequenceinfobinary from sequenceinfofull where sequenceid like '"
        + sequenceIdentifier + "%'";

    Vector byteArrayVector = getSequenceInfoQueryResultInVector(query);
    int size = byteArrayVector.size();

    if (size == 0) {
      return null;
    }

    WsrmSequenceInfoImpl[] wsrmSequenceInfoImpl =
        new WsrmSequenceInfoImpl[size];
    for (int i = 0; i < size; i++) {
      byte[] byteArray = (byte[]) byteArrayVector.remove(0);
      wsrmSequenceInfoImpl[i] = new WsrmSequenceInfoImpl(byteArray);
    }
    return wsrmSequenceInfoImpl;
  }

  /** Retrieves the SequenceInfo corresponding to the specified
      addressingIdentifier. This method throws an exception under the
      following conditions.
      <br> The Sequence specified in addressingIdentifier is an unknown one.
      <br> Storage related problems */
  public WsrmSequenceInfo
      getSequenceInfoUsingAddressingIdentifier(String addressingIdentifier) throws
      WsrmStorageException {

    String query =
        "select sequenceinfobinary from sequenceinfofull where addressingid='"
        + addressingIdentifier + "'";
    Vector byteArrayVector = getSequenceInfoQueryResultInVector(query);
    if (byteArrayVector.size() == 0) {
      return null;
    }

    byte[] byteArray = (byte[]) byteArrayVector.remove(0);

    WsrmSequenceInfoImpl wsrmSequenceInfoImpl =
        new WsrmSequenceInfoImpl(byteArray);

    return wsrmSequenceInfoImpl;

  }

  private String[] getQueryResultForSequenceInfoIdentifier(String query) throws
      WsrmStorageException {
    PreparedStatement preparedStatement =
        wsrmQueryHandler.getPreparedStatement(query);
    ResultSet resultSet = wsrmQueryHandler.getResultSet(preparedStatement);

    String[] resultString = wsrmQueryHandler.getStringArray(resultSet);
    wsrmQueryHandler.closeResultSet(resultSet);
    wsrmQueryHandler.closePreparedStatement(preparedStatement);
    return resultString;

  }

  /** Retrieves the entire list of sent/received sequence identifiers
      registered with the underlying service. This method throws an exception
      if  there are storage related problems. */
  public String[] getListOfAllSequences(boolean source) throws
      WsrmStorageException {
    String query =
        "select sequenceid from sequenceinfolight where sequencesource =" +
        wsrmQueryHandler.getIntOfBoolean(source);
    return getQueryResultForSequenceInfoIdentifier(query);

  }

  /** Retrieves the list of sent/received sequences that have expired. This
      method throws an exception if there are storage related problems. */
  public String[] getListOfExpiredSequences(boolean source) throws
      WsrmStorageException {
    String query =
        "select sequenceid from sequenceinfolight where sequencesource =" +
        wsrmQueryHandler.getIntOfBoolean(source) + " and sequenceexpired <> 0";
    return getQueryResultForSequenceInfoIdentifier(query);
  }

  /** Retrieves the list of sent/received sequences that have timed-out. This
      method throws an exception if there are storage related problems. */
  public String[] getListOfTimedOutSequences(boolean source) throws
      WsrmStorageException {
    String query =
        "select sequenceid from sequenceinfolight where sequencesource =" +
        wsrmQueryHandler.getIntOfBoolean(source) + " and sequencetimeout <> 0";
    return getQueryResultForSequenceInfoIdentifier(query);
  }

  /** Retrieves the list of sent/received sequences that have terminated. This
      method throws an exception if there are storage related problems. */
  public String[] getListOfTerminatedSequences(boolean source) throws
      WsrmStorageException {
    String query =
        "select sequenceid from sequenceinfolight where sequencesource =" +
        wsrmQueryHandler.getIntOfBoolean(source) +
        " and sequenceterminated <> 0";
    return getQueryResultForSequenceInfoIdentifier(query);

  }

  /** Retrieves the list of sent/received sequences that have been
      acknowledged and were successfully terminated. These sequences are ones
      where the sequence DID NOT EXPIRE or had an INACTIVITY_TIMEOUT. Also
      the LAST MESSAGE information should be present.
      This method throws an exception if there are storage related problems. */
  public String[] getListOfServicedSequences(boolean source) throws
      WsrmStorageException {
    String query =
        "select sequenceid from sequenceinfolight where sequencesource =" +
        wsrmQueryHandler.getIntOfBoolean(source) + " and sequenceserviced <> 0";

    return getQueryResultForSequenceInfoIdentifier(query);

  }

  /** Retrieves the list of active sent/received sequences that are currently
      available at this node.  This method throws an exception if there are
      storage related problems.*/
  public String[] getListOfActiveSequences(boolean source) throws
      WsrmStorageException {
    String query =
        "select sequenceid from sequenceinfolight where sequencesource =" +
        wsrmQueryHandler.getIntOfBoolean(source) + " and sequenceactive <> 0";
    return getQueryResultForSequenceInfoIdentifier(query);
  }

  public static void main(String[] args) {
    WsrmSequenceInfoStorageOperationsImpl
        wsrmSequenceInfoStorageOperationsImpl1 = new
        WsrmSequenceInfoStorageOperationsImpl();

  }

}
